#ifndef CONDITION_VARIABLE_H_
#define CONDITION_VARIABLE_H_

#include <pthread.h>
#include <sys/time.h>

#include "Signal.hh"
#include "Mutex.hh"

//
/**
 * \file CondVar.hh
 * \author  jsk <jsk.jinsubkim@gmail.com>
 * \version 1.0
 *
 * \class CondVar CondVar.hh
 *
 * POSIX의 Condition Variable을 추상화 한다.
 */
class CondVar : public Signal
{
public:
	/**
	 * \name CondVar
	 * \param Key Condition variable에서 사용할 뮤텍스 오브젝트.
	 */
	CondVar();
	
	/**
	 * \name ~CondVar
	 */
	~CondVar();
	
	/**
	 * \name post
	 * 
	 * 신호를 보낸다.
	 * 
	 * 만약 대기 카운터가 0보다 크면 - 대기자가 있을 경우 - pthread_cond_signal 
	 * 함수를 통해서 시그널을 보내고, 0보다 크면 대기 카운터를 감소 시킨다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf post();
	
	/**
	 * \name pend
	 * 
	 * 신호를 기다린다.
	 * 
	 * 만약 대기 카운터가 0보다 작으면 - 이미 시그널이 발생한 경우 - 카운터만 
	 * 증가시키고, 0과 같거나 크면 시그널이 발생할 때까지 대기 상태(waiting state)로 
	 * 진입해 있는다.
	 * 
	 * Condition variable은 시그널을 대기하는 두가지 방법을 제공한다.
	 * 한 방법은 시그널을 무한정 기다리고, 다른 하나는 일정 시간동안만 시그널을 기다린다.
	 * 그렇지만 이 클래스에서는 전자 한가지 방법만 제공한다. 하지만 클래스를 상속시켜 변경
	 * 할 수 있도록 해야한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf pend();
	
	/**
	 * \name setTimeout
	 * \param WaitingTime 대기 시간.
	 * 
	 * 대기시간을 설정한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf setTimeout(Number WaitingTime);
	
	/**
	 * \name release
	 * 
	 * 시그널을 기다리고 있는 스레드들을 해방한다.
	 * 
	 * 만약 대기 카운터가 0보다 크면 대기 카운터를 0으로 만들고,
	 * pthread_cond_broadcast 함수를 통해서 대기 상태에 있는 스레드를 깨운다. 
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf release();
	
	/**
	 * \name preventWaiting
	 * 
	 * 시그널을 기다리는 행위를 차단한다. 
	 * 단, 이미 발생한 시그널에 대해서는 pend 메소드가 처리할 수 있다.
	 * 
	 * 대기 상태(waiting state)로 진입하는 것을 방지한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf preventWaiting();
	
	/**
	 * \name allowWaiting
	 * 
	 * 시그널을 기다릴 수 있도록 한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf allowWaiting();
	
	/**
	 * \name init
	 * 
	 * 시그널을 초기화 한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf init();
	
	/**
	 * \name tellWaiting
	 * 
	 * 대기하는 스레드드의 개수를 알려준다.
	 * 
	 * \return 
	 * 
	 * 대기하는 스레드의 개수.
	 * 양수 일경우 대기자가 있는 경우, 
	 * 음수일 경우 시그널이 발생한 경우.
	 */
	virtual Number tellWaiting();
	
// Cond
protected:
	/**
	 * \name initObj
	 * 
	 * 조건 변수를 초기화 한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf initObj();
	
	/**
	 * \name destroyObj
	 * 
	 * 조건 변수를 파기한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf destroyObj();
	
	/**
	 * \name signal
	 * 
	 * 시그널을 발생시킨다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf signal();
	
	/**
	 * \name waitSignal
	 * 
	 * 시그널을 기다린다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf waitSignal();
	
	/**
	 * \name broadcast
	 * 
	 * 대기하고 있는 모든 스레드에게 신호를 전달한다.
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf broadcast();
	
// Mutex
protected:
	/**
	 * \name getMutexObject
	 * 
	 * 뮤텍스 오브젝트를 가져온다.
	 * 
	 * \return 
	 * 
	 * 뮤텍스 오브젝트.
	 */
	virtual pthread_mutex_t* getMutexObj();
	
// Timeout
protected:
	/**
	 * \name makeSpecWaitingTime
	 * 
	 * 
	 * 
	 * \return 
	 * 
	 * 성공할 경우 RETURN_OK, 
	 * 실패할 경우 RETURN_OK가 아닌 값.
	 */
	virtual ResultOf makeSpecWaitingTime(timeval* TimeVal, timespec* TimeSpec);
	
	// Member
protected:
	/**
	 * 대기하고 있는 스레드 개수.
	 * 
	 * 양수이면 대기하고 있는 스레드가 있는 경우이고,
	 * 0이면 대기하고 있는 스레드가 없는 경우 이고,
	 * 음수이면 처리되지 않은 시그널 개수이다.
	 */
	Number NumOfWaiting;
	
	/**
	 * 시그널을 대기할 시간.
	 * 
	 * 0일 경우 시그널 자체를 기다리지 않고,
	 * -1일 경우 무한정 시그널을 기다린다.
	 */
	Number WaitingTime;
	
	/**
	 * 조건 변수 오브젝트.
	 */
	pthread_cond_t ObjOfCondVar;
	
	/**
	 * 뮤텍스 오브젝트.
	 */
	Mutex LockOfWaiting;
	
	/**
	 * 신호를 기다리는 행위를 방지하는 나타낸다.
	 * 
	 * TRUE일 경우 pend 메소드는 신호를 기다리기 위해서 대기 상태로 진입하는 것을 
	 * 방지한다. 반대로 FALSE일 경우 대기 상태로 진입할 수 있다.
	 */
	bool BlockingWaiting;
	
	/**
	 * 인스턴스의 상태를 나타낸다.
	 * TRUE일 경우 정상, FALSE일 경우 비정상.
	 */
	bool State;
	
	// Constant
private:
	static const Number MaxNanoSec = 1000000000;
};

#endif //CONDITION_VARIABLE_H_